package org.pettyfox.pfpay.utils;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.codehaus.plexus.util.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;

/**
 * 实现分布式空闲-占用队列间转换，
 * （固定时间队列消费模式）消费空闲队列后，数据会装入占用队列，占用队列在指定时间会转换为空闲队列
 *
 */
@Service
@Slf4j
public class FreeBusyRedisQueue {
    /**
     * 占用队列数据过期时间,单位分钟
     */
    private static final int BUSY_TIMEOUT = 60;
    private static final String DEFAULT_GROUP="group";
    /**
     * 缓存队列key的前缀
     */
    private static String QUEUE_PREFIX = "FBQUEUE-";

    /**
     * 空闲队列前缀
     */
    private static String FREE_QUEUE_PREFIX = QUEUE_PREFIX+"FREE-";
    /**
     * 占用状态队列前缀
     */
    private static String BUSY_QUEUE_PREFIX = QUEUE_PREFIX+"BUSY-";


    /**
     * 缓存队列组key的前缀，多个 空闲队列可以按组的方式管理
     */
    private static String QUEUE_GROUP_FREE_PREFIX = "FBGROUP-FREE-";
    /**
     * 缓存队列组key的前缀，多个 占用队列可以按组的方式管理
     */
    private static String QUEUE_GROUP_BUSY_PREFIX = "FBGROUP-BUSY-";
    Timer timer =new Timer();
    public class ExpireTimerTask extends TimerTask {
        private String key;
        private String value;
        private Date expire;
        public ExpireTimerTask(String key,String value,Date expire){
            this.key = key;
            this.value = value;
            this.expire = expire;
        }
        @Override
        public void run() {
            if(setOperations.isMember(BUSY_QUEUE_PREFIX+key,value)){
                setOperations.remove(BUSY_QUEUE_PREFIX+key,value+"#"+expire.getTime());
                setOperations.add(FREE_QUEUE_PREFIX+key,value);
                log.debug("归还占用队列到空闲:{},{}",key,value);
            }else{
                log.debug("占用队列中数据已经被处理:{},{}",key,value);
            }
        }
    }
    @Resource
    RedisTemplate redisTemplate;

    SetOperations<String,String> setOperations;

    public synchronized void updateQueue(Set<String> freeList, String key){
        updateQueue(freeList,key,DEFAULT_GROUP,false);
    }
    public synchronized void initQueue(Set<String> freeList, String key){
        updateQueue(freeList,key,DEFAULT_GROUP,true);
    }

    public synchronized void updateQueue(Set<String> freeList, String key,String group,Boolean isInit){
        if(null == freeList || freeList.size() == 0){
            return;
        }
        setOperations = redisTemplate.opsForSet();
        //旧的占用队列，可能存在已过期未清理的数据
        Set<String> redisBusyList = setOperations.members(BUSY_QUEUE_PREFIX+key);
        Long currentTime = System.currentTimeMillis();
        //旧的未过期数据
        Set<String> normalOldBusyList = new HashSet<>();
        //旧的已过期数据,应该在旧的空闲队列中
        Set<String> expireOldBusyList = new HashSet<>();
        if(null != redisBusyList && redisBusyList.size()>0){
            for(String busy : redisBusyList){
                String[] values = busy.split("#");
                Long expireTime = Long.valueOf(values[1]);
                if(expireTime > currentTime ){
                    //未过期
                    normalOldBusyList.add(values[0]);
                    if(isInit){
                        log.debug("初始化，添加占用队列过期监听");
                        Date expire = new Date(expireTime);
                        ExpireTimerTask expireTimerTask = new ExpireTimerTask(key,busy,expire);
                        timer.schedule(expireTimerTask,expire);
                    }
                }else{
                    //已过期
                    expireOldBusyList.add(values[0]);
                    setOperations.remove(BUSY_QUEUE_PREFIX+key,busy);
                }
            }
        }

        log.debug("正常占用队列旧数据数量:{}",normalOldBusyList.size());
        log.debug("过期占用队列旧数据数量:{}",expireOldBusyList.size());

        Set<String> tempList = new HashSet<>();
        tempList.clear();
        tempList.addAll(normalOldBusyList);
        //清理正在占用队列但不存在于新的空闲队列
        tempList.removeAll(freeList);
        tempList.stream().map(r->{
            log.debug("清理正在占用，但不在于新空闲队列中的数据:{}",r);
            setOperations.remove(BUSY_QUEUE_PREFIX+key,r);
            return r;
        });
        tempList.clear();
        tempList.addAll(normalOldBusyList);
        //空闲队列中踢出正在占用的队列
        freeList.removeAll(tempList);//此时空闲队列和，占用队列是最准确的

        tempList.clear();
        tempList.addAll(freeList);
        //旧空闲列表
        Set<String> oldFreeList = setOperations.members(FREE_QUEUE_PREFIX+key);

        //redis要新增的
        tempList.removeAll(oldFreeList);
        tempList.stream().forEach(r->{
            log.debug("空闲队列新增数据:{}",r);
            setOperations.add(FREE_QUEUE_PREFIX+key,r);
        });

        //要删除的
        tempList.clear();
        tempList.addAll(oldFreeList);
        tempList.removeAll(freeList);

        tempList.stream().map(r->{
            log.debug("空闲队列删除数据:{}",r);
            setOperations.remove(FREE_QUEUE_PREFIX+key,r);
            return r;
        });

        setOperations.add(QUEUE_GROUP_FREE_PREFIX+group,FREE_QUEUE_PREFIX+key);
        setOperations.add(QUEUE_GROUP_BUSY_PREFIX+group,BUSY_QUEUE_PREFIX+key);

    }

    /**
     * 取出空闲队列中的一个数据
     * @param key
     * @param expire
     * @return
     */
    public String popFreeQueue(String key,Date expire){
        String value = setOperations.pop(FREE_QUEUE_PREFIX+key);
        if(!StringUtils.isEmpty(value)){
            //移到占用队列,占用到期自动归还
            setOperations.add(BUSY_QUEUE_PREFIX+key,value+"#"+expire.getTime());
            ExpireTimerTask expireTimerTask = new ExpireTimerTask(key,value,expire);
            timer.schedule(expireTimerTask,expire);
        }
        return value;
    }
    public JSONObject realQueueInfo(){
        return realQueueInfo(DEFAULT_GROUP);
    }
    public JSONObject realQueueInfo(String group){
        JSONObject result = new JSONObject();
        Set<String> freeGroupList = setOperations.members(QUEUE_GROUP_FREE_PREFIX+group);
        Set<String> busyGroupList = setOperations.members(QUEUE_GROUP_BUSY_PREFIX+group);
        result.put("freeGroupList",freeGroupList);
        result.put("busyGroupList",busyGroupList);
        JSONObject freeGroupListDetail = new JSONObject();
        freeGroupList.stream().forEach(key->{
            freeGroupListDetail.put(key,redisTemplate.opsForSet().members(key));
        });
        JSONObject busyGroupListDetail = new JSONObject();
        busyGroupList.stream().forEach(key->{
            busyGroupListDetail.put(key,redisTemplate.opsForSet().members(key));
        });
        result.put("freeGroupListDetail",freeGroupListDetail);
        result.put("busyGroupListDetail",busyGroupListDetail);
        return result;
    }
}
